# -*- coding:utf-8 -*-

"""
基础请求类
"""
import json

import requests
import time
from channels.layers import get_channel_layer
import asyncio

from requests import Response, request

from Apps.Sk.user.skMenu import SK_MENU
from Apps.common.interface.customScript import CustomScript
from Apps.common.interface.interfaceEnv import interfaceEnv
from Apps.common.interface.interfaceJsonDeleteExplanatory import interface_json_delete_explanatory
import logging

from Apps.util.snowFlake import get_guid

logger = logging.getLogger("django")

channel_layer = get_channel_layer()


async def send_request_result(request_group, return_result):
    await channel_layer.group_send(f"user_{request_group}", {"type": "send.result", "message": return_result})


class RequestBase:
    def __init__(self, env, interface_data):

        self.env = env
        # 接口ID
        self.interface_case_id = interface_data["id"]
        # 请求方式
        self.method = interface_data["method"]
        # 请求地址
        self.url = interface_data["path"]
        # 请求头
        self.headers = []
        for item in interface_data["parameters"]["Headers"]:
            if item["key"] and item["value"]:
                self.headers.append({item["key"]: item["value"]})
        # cookies
        # self.cookies = interface_data["parameters"]["Cookies"]
        # 认证
        # self.auth = auth
        # url参数
        # self.params = interface_data["parameters"]["Params"]
        self.params = []
        for item in interface_data["parameters"]["Params"]:
            if item["key"] and item["value"]:
                self.params.append({item["key"]: item["value"]})

        # post正文data
        self.data = interface_data["requestBody"]["text"]["string"]
        # python字典
        self.json = interface_data["requestBody"]["text"]["string"]
        # self.files = files
        # self.proxies = proxies
        # self.verify = verify
        self.timeout = 10
        self.allow_redirects = True

        # 前置脚本
        self.preProcessors = interface_data["preProcessors"]["string"]

        # 后置脚本
        self.postProcessors = interface_data["postProcessors"]["string"]
        # 响应
        self.response = None
        # 控制台输入数据
        self.console_out = ""

    def request_api(self, *args, **kwargs):
        """
        method:
        url:
        params:（可选）字典、要发送的元组或字节列表
                在：class:`Request`的查询字符串中。
        data:（可选）字典、元组、字节或类似文件的列表
                要在：class:`Request`的正文中发送的对象。
        json:（可选）要在：class:`Request`的正文中发送的JSON可序列化Python对象。
        headers:（可选）要使用：class:`Request`发送的HTTP标头字典。
        cookies:（可选）要使用：class:`Request`发送的Dict或CookieJar对象。
        files:（可选）用于多部分编码上传的```name'：类文件对象``（或``{'name'：文件元组}`）字典。
                ``文件元组``可以是2元组``（'filename'，fileobj）``，3元组````
                或四元组``（'filename'，fileobj，'content_type'，custom_headers）``，其中`'content-type``是字符串
                定义给定文件的内容类型和“custom_headers”（包含附加头的dict类对象）
                为文件添加。
        auth:（可选）用于启用基本/摘要/自定义HTTP身份验证的身份验证元组。
        timeout:(浮点数，或者元祖)（可选）等待服务器发送数据的秒数
                在放弃之前，作为浮点或：ref:`（连接超时，读取
                timeout）<timeouts>`元组。
        allow_redirects:(布尔值)（可选）布尔。启用/禁用get/options/post/put/patch/delete/head重定向。默认为“true”。
        proxies:（可选）到代理URL的字典映射协议。
        verify:（可选）布尔值，在这种情况下，它控制我们是否验证
                服务器的TLS证书或字符串，在这种情况下，它必须是路径
                到要使用的CA捆绑包。默认为`True``。
        stream:（可选）如果“False”，将立即下载响应内容。
        """
        if self.method.lower() == "get":
            result = request("get", url=self.url, params=self.params, *args, **kwargs)
            return result

        if self.method.lower() == "options":
            result = request("options", url=self.url, *args, **kwargs)
            return result

        if self.method.lower() == "head":
            # kwargs.setdefault("allow_redirects", False)
            result = request("head", url=self.url, *args, **kwargs)
            return result

        if self.method.lower() == "post":
            result = request("post", url=self.url, data=self.data, json=self.json, *args, **kwargs)
            return result

        if self.method.lower() == "put":
            result = request("put", url=self.url, data=self.data, *args, **kwargs)
            return result

        if self.method.lower() == "patch":
            result = request("patch", url=self.url, data=self.data, *args, **kwargs)
            return result

        if self.method.lower() == "delete":
            result = request("delete", url=self.url, *args, **kwargs)
            return result

        raise Exception("Unknown method")

    def request(self):
        # 请求接口前,脚本解析
        CustomScript(script=self.preProcessors, request=self, env=self.env).run()
        # 请求接口
        start_time = time.time()
        self.response: Response = self.request_api()
        end_time = time.time()

        # 接口请求时间
        response_time = end_time - start_time

        # 请求接口后,脚本解析
        self.console_out += '\n'
        CustomScript(script=self.postProcessors, request=self, response=self.response).run()

        logger.info("######  脚本输出")
        logger.info(self.console_out)
        logger.info(f"响应头Content-Type：{self.response.headers['Content-Type']}")
        logger.info(self.response.headers)
        return {
            # 响应的ID
            "response_id": get_guid(),
            # 接口用例ID
            "interface_case_id": self.interface_case_id,
            # 响应时间
            "response_time": int(response_time * 1000),
            # status
            "response_status": int(self.response.status_code),
            # 响应长度
            "response_size": len(self.response.text),
            # 响应头
            "response_headers": json.dumps(dict(self.response.headers)),
            # 响应体
            "response_body": str(self.response.text),
            # 控制台
            "console_out": self.console_out,

            "request_method": self.method,
            "request_url": self.url,
            "request_body": self.data,
            "request_headers": self.headers,
        }


class RunInterfaceRequest(object):

    def __init__(self, env=None):
        # 接口模型InterfaceTest 的 ID
        self.interface_id = ""
        # 存放接口信息，从数据库中查询，或者直接请求时给过来的数据
        self.Interface_test_data = {}
        self.env = env

    def template_replace(self):
        """
        模板替换
        """
        pass

    def run_request(self, user, interface_data):
        """

        """

        # 初始化环境变量
        if self.env is None:
            self.env = interfaceEnv(project_id=interface_data["project"])

        # 去除json中的注释
        interface_data = interface_json_delete_explanatory(interface_data)

        logger.info(f"请求的数据原始数据：{interface_data}")

        # 模板替换，URL，Params，Headers，body，Auth

        # 实例化请求类
        request_return_result = RequestBase(env=self.env, interface_data=interface_data).request()

        logger.info(f"请求操作人：{user.id, user.nickname}")
        logger.info(type(request_return_result))
        logger.info(request_return_result)

        asyncio.run(send_request_result(user.id, {"type": SK_MENU["RequestReturnResult"],
                                                  "data": json.dumps(request_return_result)}))


"""
将对象 json.dumps直接转换为字符串时，会报错
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type CaseInsensitiveDict is not JSON serializable
"""

"""
{
    id: this.currentActiveTab,
    path: "https://www.baidu.com/",
    method: "GET",
    name: "undefined",
    parameters: {
        Params: [{key: "", value: "", description: "", enable: true}],
        Headers: [{key: "", value: "", description: "", enable: true}]
    },
    requestBody: {
        type: "application/json",
        // json,xml,plain,html
        text: {
            string: ""
        },
        // form-data,x-www-form-urlencoded
        form_data: [{key: "", value: "", description: "", enable: true}],
        // octet-stream
        octet_stream: []
    },
    preProcessors: {
        string: ""
    },
        postProcessors: {
        string: ""
    },
    advancedSettings: {
        env: ""
    },
    cat: catId ? catId : this.selectedInterfaceCatId,
    project: currentProjectId.value
}
"""
